module Collision

module Bresenham

  def bresenham(x0, y0, x1, y1, &block)
    dy = y1 - y0
    dx = x1 - x0

    if dy < 0 then dy = -dy;  stepy = -1 else stepy = 1 end
    if dx < 0 then dx = -dx;  stepx = -1 else stepx = 1 end
    dy <<= 1                                                  # dy is now 2*dy
    dx <<= 1                                                  # dx is now 2*dx

    #raster.setPixel(pix, x0, y0);
    yield x0, y0 if block_given?
    if dx > dy
      fraction = dy - (dx >> 1);                         # same as 2*dy - dx
      while x0 != x1
        if fraction >= 0
          y0 += stepy
          fraction -= dx                               # same as fraction -= 2*dx
        end
        x0 += stepx
        fraction += dy                                  # same as fraction -= 2*dy
      #  raster.setPixel(pix, x0, y0);
      yield x0, y0 if block_given?   
      end
    else
      fraction = dx - (dy >> 1)
      while y0 != y1
        if fraction >= 0
          x0 += stepx
          fraction -= dy
        end
        y0 += stepy
        fraction += dx
        #raster.setPixel(pix, x0, y0);
        yield x0, y0 if block_given?  
      end
    end
    if block_given?  
      yield x0, y0
    else
      return [x0, y0]
    end
  end
  
  module_function :bresenham

end

class CollisionMap
  attr_reader :width, :height, :inverted, :image
  def initialize(filename="", data=false)
    @inverted=false
    @width=0
    @height=0
    if data
      @image=filename
    else
      if filename!="" and File.exists?(filename)
        load(filename)
      else
        raise
      end
    end
  end
  def load(filename)
    @image=EasyBMP::BMP.new
    if not @image.ReadFromFile(filename)
      @image=nil
      return false
    end
    if @image.TellNumberOfColors !=2
      @image.SetBitDepth(1)
    end
    @width=@image.TellWidth
    @height=@image.TellHeight
  end
  def unload
    @width=0
    @height=0
    @image=nil
  end
  def invert(toggle=true)
    @inverted=toggle
  end
  def invert=(toggle)
    @inverted=toggle    
  end
  def traversable?(x,y)
    n = @image.at(x,y)
    return ((n.Red==255 and n.Green==255 and n.Blue==255) ^ @inverted)
  end
  def [] (x,y)
    n = @image.at(x,y)
    return (n.Red==255 and n.Green==255 and n.Blue==255)
  end
  def at(x,y)
    n = @image.at(x,y)
    return (n.Red==255 and n.Green==255 and n.Blue==255)
  end
  def each
    @image.TellWidth.times { |x|
      @image.TellHeight.times { |y|
        n = @image.at(x,y)
        yield(x, y) if ((n.Red==255 and n.Green==255 and n.Blue==255) ^ @inverted)
      }
    }
  end
  def fsize
    return @height*@width*(8*4)
  end
  def size
    return @height*@width
  end
  def print
  end
end

class AnimatedCollisionMap
  DefRoot="data/characters/collisions.def"
  attr_reader :animObj,:name, :speed, :width, :height, :loop
  @@animations={}
  def initialize(name, file, speed, x, y)
    @animObj=[]
    @name=name
    @width,@height=x,y
    posx=0
    loadbitmap=EasyBMP::BMP.new
    loadbitmap.ReadFromFile(file)
    bitmap=EasyBMP::BMP.new
    bitmap.SetSize(@width, @height)
    (loadbitmap.TellWidth/@width).times { |file|
      EasyBMP::RangedPixelToPixelCopy(loadbitmap, posx, posx+@width, 0, @height, bitmap, 0, 0)
      @animObj.push(CollisionMap.new(bitmap,true))
      posx+=@width
    }
    @speed=speed
    @started=false
    @done=false
    @@animations[@name]=self
  end
  def invert(toggle=true)
    @animObj.each { |obj|
      obj.invert(toggle)
    }
  end
  def invert=(toggle)
    @animObj.each { |obj|
      obj.invert(toggle)
    } 
  end
  def object
    return @animObj
  end
  def traversable?(frame,x,y)
    return (@animObj[frame].traversable?(x,y))
  end
  def restart
    @started=false
    @done=false
  end
  def draw#(x,y)
    if not @started
      start=(Gosu::milliseconds / @speed % @animObj.size)
      @started=true
      @map=[]
      i=0
      start.upto(@animObj.size-1) { |index|
        @map[index]=i
        i+=1
      }
      if start-1>=0
        0.upto(start-1) { |index|
          @map[index]=i
          i+=1
        }
      end
    end
    index=@map[(Gosu::milliseconds / @speed % @animObj.size)]
    if not @done and index < @animObj.size - 1 #single play mode
      return @animObj[index]
    else
      @done=true
      return @animObj[@animObj.size - 1]
    end
  end
  def AnimatedCollisionMap.get(at)
    if @@animations[at] !=nil
      return @@animations[at]
    else
      $flog.error("Animation") {"Animation resource '#{at}' does not exist."}
      $flog.warn("Animation") {"Sending null animation resource as an alternative."}
      return Animation.new($window,"","null.png",5000,1,1)
    end
  end
  def AnimatedCollisionMap.deleteAllAnimations
    @@animations={}
  end
end

class NodeMap
  attr_reader :width, :height
  def initialize(bumpmap, nodex, nodey)
    loadCollisionMap(bumpmap, nodex, nodey)
  end
  def loadCollisionMap(bumpmap, nx, ny)
    @nodex=nx
    @nodey=ny
    @width=(bumpmap.width.quo(@nodex)).ceil
    @height=(bumpmap.height.quo(@nodey)).ceil
    @array=Array.new(@width,Array.new(@height,true))
    nx=0
    ny=0
   # traversable=true
    bumpmap.width.times { |w|
      nx=(w/@nodex).floor
    #  traversable=true
      bumpmap.height.times { |h|
        ny=(h/@nodey).floor
        @array[nx][ny]=true
        if not bumpmap.traversable?(w,h)
          @array[nx][ny]=false
          break
        end
      }
    }
  end
  def nodes
    return @array
  end
  def nodes=(array)
    @array=array
  end
  def nodeWidth
    return @nodex
  end
  def nodeHeight
    return @nodey
  end
  def nodeAt(x,y)
    if x < 0 or y < 0
      return nil
    end
    if x > @nodex*@width or y > @nodey*@height
      return nil
    end
    return [(x/@nodex).floor, (y/@nodey).floor]
  end
  def traversable?(x,y)
    if @array[x]!=nil
      return @array[x][y]
    else
      return nil
    end
  end
  def nodeCenter(x,y)
    return [(@nodex*(x+0.5)).round, (@nodey*(y+0.5)).round]
  end
  def nodeTopLeft(x,y)
    return [@nodex*x, @nodey*y]
  end
  def nodeBottomRight
    return [@nodex*(x+1), @nodey*(y+1)]
  end
  def [] (x,y)
    return traversable?(x,y)
  end
  def at(x,y)
    return traversable?(x,y)
  end
  def raycast(x1, y1, x2, y2) #Bresenham algorithm w/ vexel paging
    last=[2]
    Bresenham.bresenham(x1, y1, x2, y2) { |x, y|
      if not traversable?(nodeAt(x,y)[0],nodeAt(x,y)[1])
        return nodeAt(x,y)
      else
        last=nodeAt(x,y)
      end
    }
    return last
  end
end

end #module Collision

include Collision